//
//  FMTable.m
//  面向对象的数据库操作
//
//  Created by appbanana on 2017/3/18.
//  Copyright © 2017年 appbanana. All rights reserved.
//

#import "FMTable.h"
#import "FMTableTool.h"
#import "FMModelTool.h"
#import "FMSqliteTool.h"

@implementation FMTable

+ (BOOL)createTableWithModelClass:(Class)cls uid:(NSString *)uid{
    
    //1.判断表是否存在
    // 1.1 表不存在 创建
    // 1.2表存在 而且不要更新 返回
    // 1.3 表存在 需要更新
    
    //  1.1表不存在
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    BOOL isTableExists = [FMTableTool isTableExists:tableName uid:uid];
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要使用这个框架, 操作你的模型, 必须要实现+ (NSString *)primaryKey;");
        return NO;
    }
    NSString *primaryKey = [[cls new] primaryKey];
    if (!isTableExists) {
        NSString *string = [FMModelTool modelIvarNameAndSqliteTypeStr:cls];
        NSString *createSql = [NSString stringWithFormat:@"create table if not exists %@ (%@, primary key(%@));", tableName, string, primaryKey];

        return [FMSqliteTool dealSql:createSql uid:uid];
    }
    
    // 1.2表存在 而且不要更新
    if (isTableExists && ![self isTableRequireUpdate:cls uid:uid]) {
        NSLog(@"表格存在而且不需要更新");
        return YES;
    }
    // 1.3表存在 需要更新
    NSString *tmpTableName = [FMModelTool tmpTableNameWithClass:cls];
    NSString *ivarNameAndSqliteType = [FMModelTool modelIvarNameAndSqliteTypeStr:cls];
    // 创建临时表 插入主键 update其他值 删除旧表 更改表名
    NSMutableArray *updateSqlStr = [NSMutableArray arrayWithCapacity:1];
    NSString *createSql = [NSString stringWithFormat:@"create table if not exists %@ (%@, primary key(%@));", tmpTableName, ivarNameAndSqliteType, primaryKey];

    [updateSqlStr addObject:createSql];
    
    //插入主键
    NSString *insertSql = [NSString stringWithFormat:@"insert into %@(%@) select %@ from %@;", tmpTableName, primaryKey, primaryKey, tableName];
    [updateSqlStr addObject:insertSql];
    
    //更新
    NSArray *columnNames = [FMTableTool sortedColumnName:tableName uid:uid];
    NSArray *modelColumnArray = [FMModelTool modelIvarSortedName:cls];
    NSDictionary *renameDic = [[cls new] renameNewKeyToOldKeyDic];
    
    for (NSString *newColumnName in modelColumnArray) {
        NSString *oldColumName = renameDic[newColumnName];
        if (oldColumName.length == 0 || ![columnNames containsObject:oldColumName]) {
            oldColumName = newColumnName;
        }
        if ([columnNames containsObject:oldColumName] || [columnNames containsObject:newColumnName]) {
            NSString *updateSql = [NSString stringWithFormat:@"update %@ set %@ = (select %@ from %@ where %@.%@ = %@.%@);", tmpTableName, newColumnName, oldColumName, tableName, tmpTableName, primaryKey, tableName, primaryKey];
            [updateSqlStr addObject:updateSql];
        }
        
    }
    
    NSString *dropOldTable = [NSString stringWithFormat:@"drop table if exists %@;", tableName];
    [updateSqlStr addObject:dropOldTable];
    
    NSString *renameSql = [NSString stringWithFormat:@"alter table %@ rename %@;", tmpTableName, tableName];
    [updateSqlStr addObject:renameSql];
    
    return [FMSqliteTool dealSqls:updateSqlStr uid:uid];
}


+ (BOOL)saveOrUpdate:(id)model uid:(NSString *)uid{
    Class cls = [model class];
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    //1.首先看表是否存在 不存在就要创建表
    BOOL result = [self isTableExists:cls uid:uid];
    if (!result) {
        [self createTable:cls uid:uid];
    }
    //2.看表是否需要更新
    if ([self isTableRequireUpdate:cls uid:uid]) {
        [self updateTable:cls uid:uid];
    }
    //3.一定存在该表,主键判断表中是否有这条数据
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要使用这个框架, 操作你的模型, 必须要实现+ (NSString *)primaryKey;");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryKeyValue = [model valueForKeyPath:primaryKey];
    //select * from Student where stu_id = '2';
    NSString *querySql = [NSString stringWithFormat:@"select * from %@ where %@ = '%@';",tableName, primaryKey, primaryKeyValue];
    NSArray *rowArray = [FMSqliteTool querySql:querySql uid:uid];
    //所有字段的名称
    NSArray *columnArry = [FMModelTool modelIvarNameAndIvarType:cls].allKeys;
    
    //所有字段对应的value值
    NSMutableArray *columnValues = [NSMutableArray arrayWithCapacity:1];
    for (NSString *columnKey in columnArry) {
        id value = [model valueForKey:columnKey];
        NSLog(@"-----%@", value);
        // 对value, 进行处理
        // 处理方式: 把字典/数组 -> 字符串进行存储
        // 1. 获取这一列真正的类型
        //        NSString *type = ivarNameTypeDic[columnName];
        // 2. 判断, 数组/字典 -> 字符串
        if ([value isKindOfClass:[NSArray class]] || [value isKindOfClass:[NSDictionary class]]) {
            
            NSData *data = [NSJSONSerialization dataWithJSONObject:value options:NSJSONWritingPrettyPrinted error:nil];
            value = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        }
        [columnValues addObject:value];
        

        
    }
    //3.1 如果有的话 更具主键 update
    if (rowArray.count > 0) {
        //update student set name = 'jqc', age = '18' where stu_id = '2';
        NSMutableArray *array = [NSMutableArray arrayWithCapacity:1];
        for (int i = 0; i < columnArry.count; i++) {
            NSString *key = columnArry[i];
            id value = columnValues[i];
            NSString *string = [NSString stringWithFormat:@"%@ = '%@'", key, value];
            [array addObject:string];
        }
        NSString *resultStr = [array componentsJoinedByString:@","];
        NSString *sqlStr = [NSString stringWithFormat:@"update %@ set %@ where %@ = '%@'", tableName, resultStr, primaryKey, primaryKeyValue];
        return [FMSqliteTool dealSql:sqlStr uid:uid];
        
    }
    //3.2 如果没有的话 直接插入
//    insert into Student (name, stu_id) values ('abc', '88')
    NSString *columnStr = [columnArry componentsJoinedByString:@","];
    NSString *columnValueStr = [columnValues componentsJoinedByString:@"\',\'"];
    NSString *insertIntoStr = [NSString stringWithFormat:@"insert into %@ (%@) values ('%@');", tableName, columnStr, columnValueStr];
    
    return [FMSqliteTool dealSql:insertIntoStr uid:uid];
}

+ (BOOL)deleteAllModel:(Class)cls uid:(NSString *)uid{
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *deleteSql = [NSString stringWithFormat:@"drop table %@", tableName];
    return [FMSqliteTool dealSql:deleteSql uid:uid];
}

+ (BOOL)deleteSomeModel:(id)model uid:(NSString *)uid{
    Class cls = [model class];
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要使用这个框架, 操作你的模型, 必须要实现+ (NSString *)primaryKey;");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    id primaryKeyValue = [model valueForKeyPath:primaryKey];
    //delete from Student where stu_id = '1';
    NSString *deleteSql = [NSString stringWithFormat:@"delete from %@ where %@ = '%@';", tableName, primaryKey, primaryKeyValue];
    return [FMSqliteTool dealSql:deleteSql uid:uid];
}

+ (BOOL)deleteModels:(Class)cls sql:(NSString *)sql uid:(NSString *)uid{

    return [FMSqliteTool dealSql:sql uid:uid];
}

+ (BOOL)deleteModels:(Class)cls key:(NSString *)key relationType:(RelationType)type value:(id )value uid:(NSString *)uid{
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    //delete from Student where stu_id = '1';
//    NSString *deleteSql = [NSString stringWithFormat:@"delete from %@ where %@ %@ %@;", tableName, key, [self ralationToStr][@(type)],value];
    NSString *sql = [NSString stringWithFormat:@"delete from %@ where %@ %@ '%@'", tableName, key, [self ralationToStr][@(type)], value];

    return [FMSqliteTool dealSql:sql uid:uid];
}

+ (BOOL)deleteModel:(Class)cls keys: (NSArray *)keys relations: (NSArray *)relations values: (NSArray *)values nao: (NSArray *)naos uid: (NSString *)uid{
    NSMutableString *resultStr = [NSMutableString string];
    
    for (int i = 0; i < keys.count; i++) {
        
        NSString *key = keys[i];
        NSString *relationStr = [self ralationToStr][relations[i]];
        id value = values[i];
        
        NSString *tempStr = [NSString stringWithFormat:@"%@ %@ '%@'", key, relationStr, value];
        
        [resultStr appendString:tempStr];
        
        if (i != keys.count - 1) {
            NSString *naoStr = [self sqliteLogicTypeRelation][naos[i]];
            [resultStr appendString:[NSString stringWithFormat:@" %@ ", naoStr]];
        }
    }
    
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *sql = [NSString stringWithFormat:@"delete from %@ where %@", tableName, resultStr];
    return  [FMSqliteTool dealSql:sql uid:uid];
}

+ (NSArray *)queryAllModels:(Class)cls uid:(NSString *)uid{
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    //select * from sqlite_master where type = 'table' and name = 'Student';
    NSString *sql = [NSString stringWithFormat:@"select * from  '%@';", tableName];
    NSArray <NSDictionary *>*array = [FMSqliteTool querySql:sql uid:nil];
    return [self handleResults:array toModelClass:cls];
}

+ (NSArray *)queryModels:(Class)cls key:(NSString *)key relation:(RelationType)type value:(id)value uid:(NSString *)uid{
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *sql = [NSString stringWithFormat:@"select * from %@ where %@ %@ '%@'", tableName, key, [self ralationToStr][@(type)], value];
    NSArray <NSDictionary *>*array = [FMSqliteTool querySql:sql uid:nil];

    return [self handleResults:array toModelClass:cls];
}

+ (NSArray *)queryModels:(Class)cls keys: (NSArray *)keys relations: (NSArray *)relations values: (NSArray *)values nao: (NSArray *)naos uid: (NSString *)uid{
    
    NSMutableString *resultStr = [NSMutableString string];
    for (int i = 0; i < keys.count; i++) {
        NSString *key = keys[i];
        NSString *relationStr = [self ralationToStr][relations[i]];
        id value = values[i];
        NSString *tmpStr = [NSString stringWithFormat:@"%@ %@ %@", key, relationStr, value];
        [resultStr appendString:tmpStr];
        if (i != keys.count - 1) {
            NSString *logicType = [self sqliteLogicTypeRelation][naos[i]];
            [resultStr appendString:[NSString stringWithFormat:@" %@ ", logicType]];
        }
    }
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *sql = [NSString stringWithFormat:@"select * from %@ where %@", tableName, resultStr];
    NSArray *rowArray = [FMSqliteTool querySql:sql uid:uid];
    return [self handleResults:rowArray toModelClass:cls];
}

+ (NSArray *)queryModels:(Class)cls sql:(NSString *)sqlStr uid:(NSString *)uid{
    NSArray <NSDictionary *>*array = [FMSqliteTool querySql:sqlStr uid:nil];
    return [self handleResults:array toModelClass:cls];
}


+ (NSArray *)handleResults:(NSArray *)array toModelClass:(Class)cls{
    NSMutableArray *modelArray = [NSMutableArray arrayWithCapacity:1];
    NSDictionary *ivarNameAndType = [FMModelTool modelIvarNameAndIvarType:cls];
    for (NSDictionary *rowDic in array) {
        id model = [[cls alloc] init];
        [modelArray addObject:model];
        [rowDic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
            NSString *type = ivarNameAndType[key];
            id tmpValue = value;
            if ([type isEqualToString:@"NSArray"] || [type isEqualToString:@"NSDictionary"]) {
                // array -> data -> str
                // str -> data -> array
                NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
                NSError *error;
                tmpValue = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
                if (error) {
                    NSLog(@"%@", error.localizedDescription);
                }
            }else if ([type isEqualToString:@"NSMutableArray"] || [type isEqualToString:@"NSMutableDictionary"]){
                NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
                NSError *error;
                tmpValue = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
                if (error) {
                    NSLog(@"%@", error.localizedDescription);
                }
            }
            
            [model setValue:tmpValue forKeyPath:key];
        }];
//        [model setValuesForKeysWithDictionary:rowDic];
    }
    return modelArray;
}
+ (NSDictionary *)ralationToStr {
    
    return @{
             @(RelationTypeGreater): @">",
             @(RelationTypeLess): @"<",
             @(RelationTypeEqual): @"=",
             @(RelationTypeGreaterEqual): @">=",
             @(RelationTypeLessEqual): @"<=",
             @(RelationTypeNotEqual): @"!="
             };
    
}

/**
 枚举 -> sql 逻辑运算符 映射表
 */
+ (NSDictionary *)sqliteLogicTypeRelation {
    
    return @{
             @(FMSqliteLogicTypeNot) : @"not",
             @(FMSqliteLogicTypeAnd) : @"and",
             @(FMSqliteLogicTypeOr) : @"or"
             };
    
}

+ (BOOL)createTable:(Class)cls uid:(NSString *)uid{
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *sqliteTypeStr = [FMModelTool modelIvarNameAndSqliteTypeStr:cls];
 
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要使用这个框架, 操作你的模型, 必须要实现+ (NSString *)primaryKey;");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    // create table if not exists Student2 (stu_id integer primary key, name text, age integer)
    //    create table if not exists Student3 (stu_id integer, name text, age integer, primary key(stu_id));
    NSString *sql = [NSString stringWithFormat:@"create table if not exists %@ (%@, primary key(%@));", tableName, sqliteTypeStr,primaryKey];

    return [FMSqliteTool dealSql:sql uid:uid];
}

+ (BOOL)isTableExists:(Class)cls uid:(NSString *)uid{
    //select * from sqlite_master where type = 'table' and name = 'Student';
    NSString *tableName = [FMModelTool tableNameWithClass:cls];
    NSString *sql = [NSString stringWithFormat:@"select * from sqlite_master where type = 'table' and name = '%@';", tableName];
    NSArray *rowArray = [FMSqliteTool querySql:sql uid:uid];
    return rowArray.count > 0;
}

+ (BOOL)isTableRequireUpdate:(Class)cls uid:(NSString *)uid{
    NSArray *columnArr = [FMTableTool sortedColumnName:NSStringFromClass(cls) uid:uid];
    NSArray *ivarArray = [FMModelTool modelIvarSortedName:cls];
    return ![columnArr isEqualToArray:ivarArray];
}

+ (BOOL)updateTable:(Class)cls uid:(NSString *)uid{
    NSMutableArray *sqls = [NSMutableArray arrayWithCapacity:1];
    //1.创建临时表
    NSString *oldTableName = [FMModelTool tableNameWithClass:cls];
    NSString *tmpTableName = [FMModelTool tmpTableNameWithClass:cls];
    NSString *sqliteTypeStr = [FMModelTool modelIvarNameAndSqliteTypeStr:cls];
    if (![cls respondsToSelector:@selector(primaryKey)]) {
        NSLog(@"如果想要使用这个框架, 操作你的模型, 必须要实现+ (NSString *)primaryKey;");
        return NO;
    }
    NSString *primaryKey = [cls primaryKey];
    //    create table if not exists Student3 (stu_id integer, name text, age integer, primary key(stu_id));
    NSString *createSql = [NSString stringWithFormat:@"create table if not exists %@ (%@, primary key(%@));", tmpTableName, sqliteTypeStr, primaryKey];
    [sqls addObject:createSql];
    
    //2.插入主键
//    insert into Student1 (stu_id)  select id from Student
    NSString *insertIntoSql = [NSString stringWithFormat:@"insert into %@ (%@)  select %@ from %@", tmpTableName, primaryKey, primaryKey, oldTableName];
    [sqls addObject:insertIntoSql];
    //3.根据主键更新其他对应的数据
//    update Student1 set name = (select name from Student where Student.id = Student1.stu_id)
    NSArray *newColumnArr = [FMModelTool modelIvarSortedName:cls];
    NSArray *oldColunArr = [FMTableTool sortedColumnName:oldTableName uid:uid];
    NSDictionary *renameDic;
    if ([cls respondsToSelector:@selector(renameNewKeyToOldKeyDic)]) {
        renameDic = [cls renameNewKeyToOldKeyDic];
    }
    for (NSString *newColumn in newColumnArr) {
        NSString *oldColumn = renameDic[newColumn];
        if (oldColumn == nil) {
            oldColumn = newColumn;
        }
        //如果老的表 列数组中不包含这列名 跳过 如果是主键 跳过
        if ((![oldColunArr containsObject:oldColumn] && ![oldColunArr containsObject:newColumn]) || [newColumn isEqualToString:primaryKey]) {
            continue ;
        }
        NSString *updateSql = [NSString stringWithFormat:@"update %@ set %@ = (select %@ from %@ where %@.%@ = %@.%@)", tmpTableName, newColumn, oldColumn, oldTableName, oldTableName, primaryKey, tmpTableName, primaryKey];
        [sqls addObject:updateSql];
    }
    //4.删除老表
//    drop table Student2
    NSString *deleteSql = [NSString stringWithFormat:@"drop table %@", oldTableName];
    [sqls addObject:deleteSql];
    
    //5.新的临时表改名
    //alter table Student1  rename to Student2
    NSString *sql = [NSString stringWithFormat:@"alter table %@  rename to %@", tmpTableName, oldTableName];
    [sqls addObject:sql];
    [FMSqliteTool dealSqls:sqls uid:uid];
    return YES;
}

@end
